﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using System.IO;
using System.Xml;
using System.Security.Permissions;
using Google.Apis.Analytics.v3;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Util.Store;
using System.Threading;

namespace v1_SweetGeeks.Model
{
    public unsafe static class CustomerCollection
    {
        private static string conStr = @"Data Source=tcp:mzzkl4z2qb.database.windows.net,1433;Database=GeniusgeekDB;User Id=SweetGeeks@mzzkl4z2qb;Password=!3e609952c6484641ad55a3f61dcc8a39;Trusted_Connection=False;Encrypt=True;Connection Timeout=30";
        private static double expectedMonthProfit = new double();
        private static double currentMonthProfit = new double();
        private static double lastMonthProfit = new double();
        private static double expectedProductProfit = new double();
        private static double currentProductProfit = new double();
        private static double lastProductProfit = new double();
        private static int productIterator = new int();
        private static uint productState = new int();
        private static int productViews = new int();
        private static List<string> productNames = new List<string>();
        private static string currentProductName;
        private static List<Customer> customers = new List<Customer>();

        private static void WriteErrorReport(string exMessage)
        {
            string last = "";
            try
            {
                StreamReader sr = new StreamReader("Report.txt");
                last = sr.ReadToEnd();
                sr.Close();
            }
            catch(IOException)
            {}
            StreamWriter sw = new StreamWriter("Report.txt");
            sw.WriteLine(DateTime.Now.ToString() + " : " + exMessage);
            sw.WriteLine("-----------------------------------------------------");
            sw.Write(last);
            sw.Close();
        }

        private static bool CheckIfStoredProcedureExsist(string SP)
        {
            SqlConnection con = new SqlConnection(conStr);
            SqlDataReader reader;
            SqlCommand cmd = con.CreateCommand();
            try
            {
                con.Open();
            }
            catch(SqlException myex)
            {
                WriteErrorReport(myex.Message);
            }
            catch(Exception myex)
            {
                WriteErrorReport(myex.Message);
            }
            cmd.CommandType = CommandType.Text;
            cmd.CommandText = "select NAME from sys.procedures";
            reader = cmd.ExecuteReader();

            List<string> names = new List<string>();
            while(reader.Read())
            {
                string name = (string)reader["NAME"];
                names.Add(name);
            }
            con.Close();

            bool exsist = false;
            for(int i = 0; i < names.Count; i++)
            {
                if(names[i] == SP)
                {
                    exsist = true;
                }
            }

            return exsist;
        }
        
        private static void LoadProductGoogleAnalyticsViewId(string path)
        {
            try
            {
                XmlReader reader = XmlReader.Create(new StreamReader(path));
               
                while(reader.Read())
                {
                    for(int i = 0; i < customers.Count; i++)
                    {
                        for(int j = 0; j < customers[i].tasks.Count; j++)
                        {
                            if(reader.Value.ToLower() == customers[i].tasks[j].product.name.ToLower())
                            {
                                bool foundViewID = false;
                                do
                                {
                                    reader.Read();
                                    if(reader.Name == "viewID")
                                    {
                                        reader.Read();
                                        customers[i].tasks[j].product.googleAnalyticsViewId = reader.Value;
                                        foundViewID = true;
                                    }
                                }while(!foundViewID);
                            }
                        }
                    }
                }
                reader.Close();
            }
            catch(XmlException myex)
            {
                WriteErrorReport(myex.Message);
            }
            catch(Exception myex)
            {
                WriteErrorReport(myex.Message);
            }
        }
        
        private static void LoadProductNames()
        {
            productNames.Clear();

            for(int i = 0; i < customers.Count; i++)
            {
                for(int j = 0; j < customers[i].tasks.Count; j++)
                {
                    bool exsist = false;
                    for(int k = 0; k <  productNames.Count; k++)
                    {
                        if(customers[i].tasks[j].product.name == productNames[k])
                        {
                            exsist = true;
                        }
                    }
                    if(!exsist)
                    {
                        productNames.Add(customers[i].tasks[j].product.name);
                    }
                }
            }

            currentProductName = productNames[productIterator];
        }

        private static void CalculateTotalIncome()
        {
            currentMonthProfit = 0;
            lastMonthProfit = 0;
            expectedMonthProfit = 0;
            int currentMonthToInt = DateTime.Now.Month;
            int lastMonthToInt = DateTime.Now.Month - 1 > 0 ? DateTime.Now.Month - 1 : 12;

            foreach(Customer c in customers)
            {
                foreach(Task t in c.tasks)
                {
                    if(t.created.Month == currentMonthToInt)
                    {
                        double temp = 0.0;
                        double.TryParse(t.value, out temp);
                        currentMonthProfit += temp;
                    }
                }
            }
            
            foreach(Customer c in customers)
            {
                foreach(Task t in c.tasks)
                {
                    if(t.created.Month == lastMonthToInt)
                    {
                        double temp = 0.0;
                        double.TryParse(t.value, out temp);
                        lastMonthProfit += temp;
                    }
                }
            }
            
            double percent = (currentMonthProfit / lastMonthProfit);

            expectedMonthProfit = currentMonthProfit * percent;

            if(double.IsInfinity(expectedMonthProfit) || double.IsNaN(expectedMonthProfit))
            {
                expectedMonthProfit = currentMonthProfit;
            }
        } 
        
        private static void CalculateTotalProduct()
        {
            currentProductProfit = 0;
            lastProductProfit = 0;
            expectedProductProfit = 0;
            int currentMonthToInt = DateTime.Now.Month;
            int lastMonthToInt = DateTime.Now.Month - 1 > 0 ? DateTime.Now.Month - 1 : 12;

            foreach(Customer c in customers)
            {
                foreach(Task t in c.tasks)
                {
                    if(t.created.Month == currentMonthToInt && t.product.name == currentProductName)
                    {
                        double temp = 0.0;
                        double.TryParse(t.value, out temp);
                        currentProductProfit += temp;
                    }
                }
            }
            
            foreach(Customer c in customers)
            {
                foreach(Task t in c.tasks)
                {
                    if(t.created.Month == lastMonthToInt && t.product.name == currentProductName)
                    {
                        double temp = 0.0;
                        double.TryParse(t.value, out temp);
                        lastProductProfit += temp;
                    }
                }
            }
            
            double percent = (currentProductProfit / lastProductProfit);

            expectedProductProfit = currentMonthProfit * percent;
            
            if(double.IsInfinity(expectedProductProfit) || double.IsNaN(expectedProductProfit))
            {
                expectedProductProfit = currentProductProfit;
            }
        }

        private static void CalculateProductState()
        {
            productState = 0;
            byte[] states = new byte[4];
 
            foreach(Customer c in customers)
            {
                foreach(Task t in c.tasks)
                {
                    if(t.product.name == currentProductName)
                    {
                        if(t.status == "To Do")
                        {
                            states[0]++;
                        }
                        if(t.status == "In Progress")
                        {
                            states[1]++;
                        }
                        if(t.status == "To Test")
                        {
                            states[2]++;
                        }
                        if(t.status == "Done.")
                        {
                            states[3]++;
                        }
                    }
                }
            }

            int pos = 0;
            for(int i = 0; i < 4; i++)
            {
                productState |= (uint)(states[i] << pos);
                pos += 8;
            }
        }

        private static void LoadGoogleAnayticsForProduct()
        {
            string clientId = "134199175321-bo68lv6gkg8t194pbtac9odniikgkg91.apps.googleusercontent.com";
            string clientSecret = "OVSG5a0FK220Dzyiip-acNkC";
            string gaUser = "bjo@sweetgeeks.dk";
            string gaApplication = "Sweet geeks BI";
            string oauthTokenFilestorage = "My Project Storage";

            string viewID = "";
            for(int i = 0; i < customers.Count && viewID == ""; i++)
            {
                for(int j = 0; j < customers[i].tasks.Count; j++)
                {
                    if(customers[i].tasks[j].product.name == currentProductName)
                    {
                        viewID = "ga:" + customers[i].tasks[j].product.googleAnalyticsViewId;
                    }
                }
            }

            try
            {
                if(viewID != "ga:")
                {
                    var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
                    new ClientSecrets
                    {
                        ClientId = clientId,
                        ClientSecret = clientSecret
                    },
                    new[] {AnalyticsService.Scope.AnalyticsReadonly},
                    gaUser,
                    CancellationToken.None,
                    new FileDataStore(oauthTokenFilestorage)
                    ).Result;

                    var service = new AnalyticsService(new BaseClientService.Initializer()
                    {
                        HttpClientInitializer = credential,
                        ApplicationName = gaApplication
                    });

                    string start =  DateTime.Now.AddDays(-3).ToString("yyyy-MM-dd");
                    string end = DateTime.Now.ToString("yyyy-MM-dd");
                    var query = service.Data.Ga.Get(viewID, start, end, "ga:sessions");

                    query.SamplingLevel = DataResource.GaResource.GetRequest.SamplingLevelEnum.HIGHERPRECISION;

                    var response = query.Execute();

                    if(!int.TryParse(response.TotalsForAllResults.First().Value.ToString(), out productViews))
                    {
                        productViews = 0;
                    }
                }
                else
                {
                    productViews = 0;
                }
            }
            catch(Google.GoogleApiException gaEx)
            {
                WriteErrorReport(gaEx.Message);
            }  
        }

        public static void LoadAllData()
        {
            SqlConnection con = new SqlConnection(conStr);
            try
            {
                con.Open();
            }
            catch(SqlException myex)
            {
                WriteErrorReport(myex.Message);
            }
            catch(Exception myex)
            {
                WriteErrorReport(myex.Message);
            }

            customers.Clear();
            SqlDataReader reader;
            SqlCommand cmd = con.CreateCommand();
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.CommandText = "SP_GetAllDataFromDB";
            cmd.CommandTimeout = 3000;
            cmd.Parameters.Clear();
            cmd.Notification = null;
            bool good = CheckIfStoredProcedureExsist(cmd.CommandText);

            if(good)
            {
                reader = cmd.ExecuteReader();
                while(reader.Read())
                {
                    try
                    {
                        int pID = (int)reader["P_ID"];
                        string pName = reader["P_ProductName"].ToString();
                        decimal licensPrice; decimal.TryParse(reader["P_LicensPrice"].ToString(), out licensPrice);
                        decimal mSubPrice; decimal.TryParse(reader["P_MonthlySubscriptionPrice"].ToString(), out mSubPrice);
                        decimal cRatePrice; decimal.TryParse(reader["P_ClickRatePrice"].ToString(), out cRatePrice);
                        Product p = new Product(pID, pName, licensPrice, mSubPrice, cRatePrice);

                        string status = "";
                        int s; int.TryParse(reader["T_StatusID"].ToString(), out s);
                        if(s == 1)
                        {
                            status = "To Do";
                        }
                        else if(s == 2)
                        {
                            status = "In Progress";
                        }
                        else if(s == 3)
                        {
                            status = "To Test";
                        }
                        else if(s == 4)
                        {
                            status = "Done.";
                        }
                        else
                        {
                            status = " ";
                        }
                        int tID = (int)reader["T_ID"];
                        string descr = (string)reader["T_Description"];
                        string value = (string)reader["T_ValueOfTask"];
                        DateTime dt1; DateTime.TryParse(reader["T_CreationDate"].ToString(), out dt1);
                        DateTime dt2; DateTime.TryParse(reader["T_CreationDate2"].ToString(), out dt2);
                        DateTime dt3; DateTime.TryParse(reader["T_CreationDate3"].ToString(), out dt3);
                        DateTime dt4; DateTime.TryParse(reader["T_CreationDate4"].ToString(), out dt4);
                        int click; int.TryParse(reader["T_Clicks"].ToString(), out click);
                        Task t = new Task(tID, p, status, "Task", descr, value, dt1, dt2, dt3, dt4, click);

                        int cID = (int)reader["C_ID"];
                        string cName = (string)reader["C_CustomerName"];
                        Customer c = new Customer(cID, cName);

                        c.AddTask(t);
                        customers.Add(c);
                    }
                    catch(SqlException myex)
                    {  
                        WriteErrorReport(myex.Message);
                    }
                    catch(Exception myex)
                    {  
                        WriteErrorReport(myex.Message);
                    }
                }
                reader.Close();
            }

            try
            {
                con.Close();
            }
            catch(SqlException myex)
            {
                WriteErrorReport(myex.Message);
            }
            catch(Exception myex)
            {
                WriteErrorReport(myex.Message);
            }

            LoadProductNames();
            CalculateTotalIncome();
            CalculateTotalProduct();
            CalculateProductState();
            LoadProductGoogleAnalyticsViewId("GAviewID.xml");
            LoadGoogleAnayticsForProduct();
        }

        public static double* BindToExpectedProfit()
        {
            fixed(double* ptr = &expectedMonthProfit)
            {
                return ptr;
            }
        }
        
        public static double* BindToCurrentProfit()
        {
            fixed(double* ptr = &currentMonthProfit)
            {
                return ptr;
            }
        }
        
        public static double* BindToLastProfit()
        {
            fixed(double* ptr = &lastMonthProfit)
            {
                return ptr;
            }
        }

        public static double* BindToExpectedProductProfit()
        {
            fixed(double* ptr = &expectedProductProfit)
            {
                return ptr;
            }
        }
        
        public static double* BindToCurrentProductProfit()
        {
            fixed(double* ptr = &currentProductProfit)
            {
                return ptr;
            }
        }
        
        public static double* BindToLastProductProfit()
        {
            fixed(double* ptr = &lastProductProfit)
            {
                return ptr;
            }
        }

        public static uint* BindToProductState()
        {
            fixed(uint* ptr = &productState)
            {
                return ptr;
            }
        }

        public static int* BindToProductViews()
        {
            fixed(int* ptr = &productViews)
            {
                return ptr;
            }
        }

        public static string GetProductName()
        {
            return currentProductName;
        }

        public static void IterateProductIterator()
        {
            productIterator = productIterator < productNames.Count - 1 ? productIterator + 1 : 0; 
        }
    }
}
